import {exec} from "child_process";
import path   from "path";

const PERMISSION = "READ_CALENDAR;WRITE_CALENDAR;CAMERA;CAMERA;CONTACTS;READ_CONTACTS;WRITE_CONTACTS;GET_ACCOUNTS;LOCATION;ACCESS_FINE_LOCATION;ACCESS_COARSE_LOCATION;MICROPHONE;RECORD_AUDIO;PHONE;READ_PHONE_STATE;CALL_PHONE;READ_CALL_LOG;WRITE_CALL_LOG;ADD_VOICEMAIL;USE_SIP;PROCESS_OUTGOING_CALLS;SENSORS;BODY_SENSORS;SMS;SEND_SMS;RECEIVE_SMS;READ_SMS;RECEIVE_WAP_PUSH;RECEIVE_MMS;STORAGE;READ_EXTERNAL_STORAGE;WRITE_EXTERNAL_STORAGE";


//格式化日期
Date.prototype.Format = function (fmt) {
    const o = {
        "y+": this.getFullYear(),
        "M+": this.getMonth() + 1,                      //月份
        "d+": this.getDate(),                           //日
        "h+": this.getHours(),                          //小时
        "m+": this.getMinutes(),                        //分
        "s+": this.getSeconds(),                        //秒
        "q+": Math.floor((this.getMonth() + 3) / 3),    //季度
        "S+": this.getMilliseconds()                    //毫秒
    };
    for (let k in o) {
        if (new RegExp("(" + k + ")").test(fmt)) {
            if (k === "y+") {
                fmt = fmt.replace(RegExp.$1, ("" + o[k]).substr(4 - RegExp.$1.length));
            }
            else if (k === "S+") {
                let lens = RegExp.$1.length;
                lens     = lens === 1 ? 3 : lens;
                fmt      = fmt.replace(RegExp.$1, ("00" + o[k]).substr(("" + o[k]).length - 1, lens));
            }
            else {
                fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
            }
        }
    }
    return fmt;
};

const aapt = path.resolve('./libs/aapt.exe');

async function execCmd(cmd) {
    return new Promise(resolve => {
        exec(cmd, (error, stdout, stderr) => {
            if (error) {
                resolve({err: error})
            }
            const output = `${stdout}\n${stderr}`;
            resolve({err: null, data: output.trim()})
        });
    });
}


export default class Adb {
    static async devices() {
        const devices = [];
        const cmd     = "adb devices";
        const result  = await execCmd(cmd);
        if (result.err) return devices;
        const output = result.data.split("\n");
        output.splice(0, 1);
        output.forEach(it => {
            const line = /(\S*)\s+device/.exec(it);
            if (line) devices.push(line[1])
        });
        return devices;
    }

    static async exec(cmd, serial = null) {
        const command = `adb ${serial ? `-s ${serial} ` : ' '} ${cmd}`;
        return await execCmd(command);
    }

    static async shell(cmd, serial = null) {
        const command = `adb ${serial ? `-s ${serial} ` : ' '} shell ${cmd}`;
        return await execCmd(command);
    }

    static async aapt(cmd) {
        const command = `${aapt} ${cmd}`;
        return await execCmd(command);
    }

    static async model(serial = null) {
        const result = await Adb.shell("getprop ro.product.model", serial);
        if (result.err) return "";
        return result.data;
    }

    static async brand(serial = null) {
        const result = await Adb.shell("getprop ro.product.brand", serial);
        if (result.err) return "";
        return result.data;
    }

    static async sdk(serial = null) {
        const result = await Adb.shell("getprop ro.build.version.release", serial);
        if (result.err) return "";
        return result.data;
    }

    static async api(serial = null) {
        const result = await Adb.shell("getprop ro.build.version.sdk", serial);
        if (result.err) return 14;
        return parseInt(result.data);
    }

    static async xml(apkPath) {
        const command = `${aapt} d badging ${apkPath}`;
        const info    = {
            name    : '',
            version : '',
            pkg     : '',
            launcher: ''
        };
        const result  = await execCmd(command);
        if (result.err) return info;
        const m           = /package: name='(\S+)'\s+versionCode='(\d+)'\s+versionName='(\S+)'[\s\S]+application:\s+label='(\S+)'[\s\S]+launchable-activity:\s*name='(\S+)'/m.exec(result.data);
        const matches     = result.data.match(/uses-permission:\s+name='(\S+)'/gm);
        const permissions = matches.map(it => /uses-permission:\s+name='(\S+)'/.exec(it)[1]);

        if (m) {
            info.pkg        = m[1];
            info.version    = `${m[3]}-${m[2]}`;
            info.name       = m[4];
            info.launcher   = m[5];
            info.permission = permissions;
        }
        return info;
    }

    static async grant(pkg, permissions, serial = null) {
        await  permissions.forEach(async it => {
            if (PERMISSION.indexOf(it.replace("android.permission.", "")) >= 0) {
                await  Adb.shell(`pm grant ${pkg} ${it}`, serial);
            }
        })
    }

    static async install(apkPath, args = "", serial = null) {
        const result = await Adb.exec(`install -r ${args} ${apkPath}`, serial);
        if (result.err) return {err: result.err.toString()};
        return {data: result.data.indexOf("Success") >= 0};
    }

    static async pkgExist(pkg, serial) {
        const result = await Adb.shell("pm list packages -3", serial);
        if (result.err) return false;
        return result.data.indexOf(pkg) >= 0;
    }

    static async uninstall(pkg, serial) {
        const result = await Adb.exec(`uninstall ${pkg}`, serial);
        if (result.err) {
            return result.err.toString().indexOf("Unknown package:") > 0;
        }
        return result.data.indexOf("Success") >= 0;
    }


    static async clear(pkg, serial) {
        const result = await Adb.shell(`pm clear ${pkg}`, serial);
        if (result.err) return false;
        return result.data.indexOf("Success") >= 0;
    }

    static async launch(pkg, launcher, serial) {
        const result = await Adb.shell(`am start -W ${pkg}/${launcher}`, serial);
        if (result.err) return false;
        return result.data;
    }

    static async close(pkg, serial = null) {
        const result = await Adb.shell(`am force-stop ${pkg}`, serial);
        return !result.err;
    }

    static async appAlive(pkg, serial = null) {
        const result = await Adb.shell(`ps | findstr ${pkg}`, serial);
        if (result.err) return false;
        return result.data.indexOf(pkg) >= 0;
    }
}